/*
	Date:			05/19/2012
	Author:         Fabien Olousouzian
	License:      	Microsoft Public License (Ms-PL)
	Product:        XNA Shader Composer - Project
*/

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using System.Diagnostics;
using Shader_Composer.Primitive;

namespace Shader_Composer
{
    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class GameLoop : Microsoft.Xna.Framework.Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;

        #region Attributes

        private BasicEffect _beffect;
        private BasicEffect _teffect;
        private Camera _camera;
        private Device _device;

        private DebugModel _model;
        private FPSCounter _fpsc;

        private Quad _quad;
        private Texture2D _texture;

        #endregion

        #region XNA GameLoop

        /// <summary>
        /// GameLoop Class
        /// </summary>
        public GameLoop()
        {
            graphics = new GraphicsDeviceManager(this);
            graphics.SynchronizeWithVerticalRetrace = true;
            graphics.PreferMultiSampling = true;

            //graphics.PreferredBackBufferWidth = GraphicsAdapter.DefaultAdapter.CurrentDisplayMode.Width;
            //graphics.PreferredBackBufferHeight = GraphicsAdapter.DefaultAdapter.CurrentDisplayMode.Height;
            //graphics.IsFullScreen = true;

            graphics.PreferredBackBufferWidth = 1280;
            graphics.PreferredBackBufferHeight = 720;
            graphics.IsFullScreen = false;

            graphics.PreferMultiSampling = true;
            graphics.ApplyChanges();
            Content.RootDirectory = "Content";

            //Display
            _camera = new Camera(new Vector2(graphics.PreferredBackBufferWidth, graphics.PreferredBackBufferHeight));
            _model = new DebugModel();

            //Controller
            _device = new Device();
            ResetController();

            //Others
            _fpsc = new FPSCounter(graphics.PreferredBackBufferWidth, graphics.PreferredBackBufferHeight, FPSCounter.Position.UP_RIGHT);
            _quad = new Quad(Vector3.Zero, Vector3.Backward, Vector3.Up, 50, 50);

        }

        /// <summary>
        /// Allows the game to perform any initialization it needs to before starting to run.
        /// This is where it can query for any required services and load any non-graphic
        /// related content.  Calling base.Initialize will enumerate through any components
        /// and initialize them as well.
        /// </summary>
        protected override void Initialize()
        {
            _device.Initialize(Keyboard.GetState());
            _device.Initialize(Mouse.GetState());
            _device.Initialize(GamePad.GetState(PlayerIndex.One));

            _beffect = new BasicEffect(this.GraphicsDevice);
            _beffect.VertexColorEnabled = true;

            _teffect = new BasicEffect(this.GraphicsDevice);
            _teffect.EnableDefaultLighting();

            _model.Position = new Vector3(25, 0, 25);

            this.IsMouseVisible = false;

            this.InitBeforeDraw();
            base.Initialize();
        }


        /// <summary>
        /// LoadContent will be called once per game and is the place to load
        /// all of your content.
        /// </summary>
        protected override void LoadContent()
        {
            spriteBatch = new SpriteBatch(GraphicsDevice);

            DisplayInfoDebug.Instance.Load(this.Content, @"Fonts/InfoDebug");

            //_camera.Load(this.Content, @"Fonts/InfoDebug");
            _model.Load(this.Content, @"Models/cyberzero", @"Fonts/InfoDebug");
            _fpsc.Load(this.Content, @"Fonts/FPSCounter");

            _texture = Content.Load<Texture2D>(@"Textures/plan_texture");
            _teffect.TextureEnabled = true;
            _teffect.Texture = _texture;
        }

        /// <summary>
        /// UnloadContent will be called once per game and is the place to unload
        /// all content.
        /// </summary>
        protected override void UnloadContent()
        {

        }

        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Update(GameTime gameTime)
        {
            UpdateController(ref _device);
            _model.Update(ref _device);
            _camera.Update(ref _device);

            DisplayInfoDebug.Instance.Update(ref _device);
            _fpsc.UpdateFrameRate(gameTime, ref _device);

            ResetController();
            base.Update(gameTime);
        }

        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Draw(GameTime gameTime)
        {
            InitBeforeDraw();

            //Draw 3D
            _model.DrawDebugModel(ref _camera, ref _beffect, this.GraphicsDevice);

            //DrawPlan();

            //Draw 2D - Info Debug
            _camera.AddInfoDebug();
            _model.AddInfoDebug();
            _fpsc.DrawInfoDebug(ref this.spriteBatch);

            //Info Debug
            _fpsc.IncrementFrameCounter();

            DisplayInfoDebug.Instance.DrawInfoDebug(ref this.spriteBatch);

            base.Draw(gameTime);
        }

        #endregion

        #region Methods

        /// <summary>
        /// Initialiaze the camera, graphicsDevice and basicEffect
        /// </summary>
        private void InitBeforeDraw()
        {
            GraphicsDevice.Clear(Color.Black);

            //BACKFACE DRAW
            //RasterizerState rs = new RasterizerState();
            //rs.CullMode = CullMode.None;
            //_beffect.GraphicsDevice.RasterizerState = rs;

            GraphicsDevice.BlendState = BlendState.Opaque;
            GraphicsDevice.DepthStencilState = DepthStencilState.Default;

            _beffect.Projection = _camera.ProjectionMatrix;
            _beffect.View = _camera.ViewMatrix;

            //_teffect.World = Matrix.CreateRotationX(MathHelper.ToDegrees(90));
            _teffect.View = _camera.ViewMatrix;
            _teffect.Projection = _camera.ProjectionMatrix;
        }

        private void ResetController()
        {
            Mouse.SetPosition(graphics.PreferredBackBufferWidth / 2, graphics.PreferredBackBufferHeight / 2);
        }

        private void UpdateController(ref Device _device)
        {
            _device.Update(Keyboard.GetState());
            _device.Update(Mouse.GetState());
            _device.Update(GamePad.GetState(PlayerIndex.One));

            if (_device.keyJustPressed(Keys.Escape) || _device.buttonJustPressed(Buttons.Back))
                this.Exit();
        }

        private void DrawPlan()
        {
            foreach (EffectPass pass in _teffect.CurrentTechnique.Passes)
            {
                pass.Apply();

                GraphicsDevice.DrawUserIndexedPrimitives
                    <VertexPositionNormalTexture>(
                    PrimitiveType.TriangleList,
                    _quad.Vertices, 0, 4,
                    _quad.Indexes, 0, 2);
            }
        }

        #endregion
    }
}
